package com.ccjt.efcfs.cal;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.ccjt.efcfs.cal.interest.*;

import static com.ccjt.efcfs.constant.CalculateConstant.Decimal.*;
import static com.ccjt.efcfs.constant.CalculateConstant.FINANCE_MONTH_DAY;

import com.ccjt.efcfs.dto.LoanInfo;
import com.ccjt.efcfs.dto.RepayPlanCal;
import com.ccjt.efcfs.util.DateUtils;
import com.ccjt.efcfs.util.DecimalUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 基础算法类
 */
public abstract class CalculateBase {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private Boolean isSysPrint = Boolean.FALSE;


    protected LoanInfo loan = null;//贷款计划

    //    private final List<LoanInfo> loanList = new ArrayList<LoanInfo>(); //贷款计划
    private void init() {
    }

    public CalculateBase(LoanInfo loan) {
        this.loan = loan;
    }

    public LoanInfo getLoan() {
        return loan;
    }

    public void setSysPrint(Boolean sysPrint) {
        isSysPrint = sysPrint;
    }

    public List<RepayPlanCal> getPlanList() {
        return getResidualPlanList(getFirstPlan());
    }

    public List<RepayPlanCal> getResidualPlanList(RepayPlanCal preRepayPlanCal) {
        List<RepayPlanCal> planList = new ArrayList<RepayPlanCal>();
        // 计划期数
        Integer planPeriods = preRepayPlanCal.getPlanPeriods().intValue(),
                prePeriod = preRepayPlanCal.getCurPeriod().intValue();
        if (isSysPrint) {
            System.out.println(String.format("orgi plan periods : %d , pre is : %d", planPeriods, prePeriod));
        }

        for (int planIndex = 0; planIndex < planPeriods; planIndex++) {
//            RepayPlanCal repayPlanCal =  getCalculatePlan(loan, planIndex, beginResidualPrincipal, beginIncreasedInterest, null, BD_ZERO);
            RepayPlanCal repayPlanCal = calCurrentPlan(preRepayPlanCal);
            if (repayPlanCal == null) {
//                break;
                continue;
            }
            planList.add(repayPlanCal);
//            beginResidualPrincipal = repayPlanCal.getEndResidualPrincipal();
//            beginIncreasedInterest = repayPlanCal.getEndIncreasedInterest();

            if (isSysPrint) {
                String period = DateUtils.formatDate(repayPlanCal.getBeginDate()) + "~" + DateUtils.formatDate(repayPlanCal.getRepayDate() == null ? repayPlanCal.getEndDate() : repayPlanCal.getRepayDate());
                int periodIndex = repayPlanCal.getCurPeriod().intValue(),
                        periodDays = repayPlanCal.getCurPeriodNatureDays().intValue();
                BigDecimal
                        principal = repayPlanCal.getActualPerRepayPrincipal(),
                        interest = repayPlanCal.getActualPerRepayInterest(),
                        total = DecimalUtils.add(principal, interest),
                        residualPrincipal = repayPlanCal.getActualEndResidualPrincipal(),
                        increasedInterest = repayPlanCal.getActualEndIncreasedInterest();

//            /*
                System.out.println(String.format("（%d） [%s], Day : %d, Total : %f, Principal : %f, Interest : %f , ResidualPrincipal : %f, IncreasedInterest : %f",
                        periodIndex,
                        period,
                        periodDays,
                        total,
                        principal,
                        interest,
                        residualPrincipal,
                        increasedInterest
                ));
                /*
                System.out.println(String.format("[%d], date : %s, MonthPerPrincipal : %f, MonthPerInterest : %f ", (planIndex + 1), DateUtils.formatDate(repayPlanCal.getEndDate()), repayPlanCal.getMonthPerRepayPrincipal(), repayPlanCal.getMonthPerRepayInterest()));
                System.out.println("plan is : " + JSON.toJSONString(repayPlanCal,
                        SerializerFeature.PrettyFormat,
                        SerializerFeature.WriteMapNullValue,
                        SerializerFeature.WriteDateUseDateFormat));
//                */
            }
//            logger.warn("index[{}], EndResidualPrincipal : {}, EndIncreasedInterest : {} ", new Object[]{Integer.valueOf(planIndex).toString(), repayPlanCal.getEndResidualPrincipal(), repayPlanCal.getEndIncreasedInterest()});
            preRepayPlanCal = repayPlanCal;

        }
        return planList;
    }

    /**
     * 计算本期还款计划
     *
     * @param prePlan 上期还款计划
     * @return
     */
    public RepayPlanCal calCurrentPlan(RepayPlanCal prePlan) {

        // 1. 初始化
        RepayPlanCal plan = getInitCurPlan(prePlan);

        // 2. 日期
        // 2.1 (贷款)末次还款日期
        Date loanEndDate = loan.getEndDate();
        // (贷款)末次还款日期校验
        if (loanEndDate != null) {
            if (loanEndDate.before(plan.getBeginDate())) {
                // 末次还款日期 < 本期开始日期，跳过本期，直接返回空
                return null;
            } else if (loanEndDate.before(plan.getEndDate())) {
                // 本期开始日期 <= 末次还款日期 < 本期结束(还款)日期 ，本期结束(还款)日期 即为 末次还款日期
                plan.setEndDate(loanEndDate);
            }
        }

        // 2.2. 提前还款日期
        Date earlyRepayDate = loan.getEarlyRepayDate();
        if (earlyRepayDate != null) {
            // 实际还款日期校验并赋值
            if (earlyRepayDate.before(plan.getBeginDate())) {
                // 实际还款日期 < 本期开始日期，跳过本期，直接返回空
                return null;
            } else if (earlyRepayDate.before(plan.getEndDate())) {
                // 本期开始日期 <= 实际还款日期 < 本期结束(还款)日期，给本次还款计划的实际还款日期赋值
                plan.setRepayDate(earlyRepayDate);
            }
        }

        // 3. 天数
        // 3.1. 本期-计划周期天数
        // 计划周期天数，按贷款发生日期、首次还款日期和末次还款日期的值计算
        BigDecimal periodCycleDays = DateUtils.getBetweenBigDecimalDays(plan.getBeginDate(), plan.getEndDate());
        plan.setCurPeriodCycleDays(periodCycleDays); // 本期计划周期天数，金融算法为30天/月

        // 3.2. 本期-自然天数
        BigDecimal periodNatureDays = plan.getRepayDate() == null ? periodCycleDays : DateUtils.getBetweenBigDecimalDays(plan.getBeginDate(), plan.getRepayDate());
        plan.setCurPeriodNatureDays(periodNatureDays); // 本期自然天数

        // 5.罚息或手续费
        /**
         * @TODO 此处需求暂时未实现，罚息、利息等，总年化要小于36%
         */

        // 6.应付利息
        calRepayInterest(plan);

        // 7. 应付本金
        calRepayPrincipal(plan);

        return plan;
    }

    /**
     * 根据不同贷款方式，计算实际应付本金
     *
     * @param plan
     * @return
     */
    protected void calRepayInterest(RepayPlanCal plan){
        // 6.1 本期计划周期应付利息
        plan.setMonthPerRepayInterest(new MonthInterestCal().calPeriodInterest(plan));
        // 6.2 本期计划周期金融日应付利息
        plan.setFinancePerRepayInterest(new FinanceInterestCal().calPeriodInterest(plan));
        // 6.3 本期周期自然日应付利息
        plan.setNaturePerRepayInterest(new NatureInterestCal().calPeriodInterest(plan));
        // 6.4 本期实际自然日应付利息
        plan.setActualPerRepayInterest(new ActualInterestCal().calPeriodInterest(plan));
    }

    /**
     * 根据不同贷款方式，计算实际应付本金
     *
     * @param plan
     * @return
     */
    protected abstract void calRepayPrincipal(RepayPlanCal plan);

    /**
     * 根据不同贷款方式，计算实际应付本金利息合计
     *
     * @return
     */
//    protected abstract void calRepayTotal(RepayPlanCal plan);
    public RepayPlanCal getFirstPlan() {
        BigDecimal loanPrincipal = loan.getLoanPrincipal();
        BigDecimal paidInterest = loan.getPaidInterest();

        RepayPlanCal plan = new RepayPlanCal();
        plan.setPlanPeriods(loan.getPlanPeriods()); // 计划期数
        plan.setCurPeriod(loan.getBeginPeriod());
        plan.setMonthBeginResidualPrincipal(loanPrincipal);
        plan.setFinanceBeginResidualPrincipal(loanPrincipal);
        plan.setNatureBeginResidualPrincipal(loanPrincipal);
        plan.setActualBeginResidualPrincipal(loanPrincipal);
        plan.setMonthBeginIncreasedInterest(paidInterest);
        plan.setFinanceBeginIncreasedInterest(paidInterest);
        plan.setNatureBeginIncreasedInterest(paidInterest);
        plan.setActualBeginIncreasedInterest(paidInterest);

        return plan;
    }

    public RepayPlanCal getInitCurPlan(RepayPlanCal prePlan) {

        RepayPlanCal curPlan = new RepayPlanCal();

        // 1.1.期初金额
        curPlan.setMonthBeginResidualPrincipal(prePlan.getMonthEndResidualPrincipal());
        curPlan.setFinanceBeginResidualPrincipal(prePlan.getFinanceEndResidualPrincipal());
        curPlan.setNatureBeginResidualPrincipal(prePlan.getNatureEndResidualPrincipal());
        curPlan.setActualBeginResidualPrincipal(prePlan.getActualEndResidualPrincipal());
        curPlan.setMonthBeginIncreasedInterest(prePlan.getMonthEndIncreasedInterest());
        curPlan.setFinanceBeginIncreasedInterest(prePlan.getFinanceEndIncreasedInterest());
        curPlan.setNatureBeginIncreasedInterest(prePlan.getNatureEndIncreasedInterest());
        curPlan.setActualBeginIncreasedInterest(prePlan.getActualEndIncreasedInterest());

        // 1.2. 期数
        curPlan.setPlanPeriods(loan.getPlanPeriods()); // 计划期数
        // 当前期数
        BigDecimal currentPeriod = prePlan.getCurPeriod().add(BD_ONE);
        curPlan.setCurPeriod(currentPeriod);

        // 1.3. 日期(计划)
        // 本期开始日期
        Date currentPeriodBeginDate = null;
        // 本期结束(还款)日期
        Date currentPeriodEndDate = null;

        // 起始首期处理
        if (loan.getBeginPeriod().equals(prePlan.getCurPeriod())) {
            // 首期开始日期=贷款发生日期
            currentPeriodBeginDate = loan.getLoanDate();
            // 首期结束(还款)日期=首次还款日期
            currentPeriodEndDate = loan.getBeginDate();
        } else {
            // 本期开始日期
            currentPeriodBeginDate = DateUtils.addMonths(loan.getBeginDate(), DecimalUtils.sub(currentPeriod, loan.getBeginPeriod()).intValue() - 2);
            // 本期结束(还款)日期（首次还款日期 + 间隔周期）
            currentPeriodEndDate = DateUtils.addMonths(loan.getBeginDate(), DecimalUtils.sub(currentPeriod, loan.getBeginPeriod()).intValue() - 1);
        }
        curPlan.setBeginDate(currentPeriodBeginDate);
        curPlan.setEndDate(currentPeriodEndDate);

        // 1.4.利率
        curPlan.setYearRate(loan.getYearRate()); // 年利率
        curPlan.setMonthRate(loan.getMonthRate()); // 月利率
        curPlan.setFinanceDayRate(loan.getDayRate()); // (金融)日利率 : 360
        curPlan.setNatureDayRate(loan.getNatureDayRate()); // (自然)日利率 : 365/366
        // @TODO 需要根据实际来判断
        curPlan.setActualDayRate(loan.getDayRate()); // (实际)日利率

        return curPlan;
    }


    /**
     * 实时计算还款计划
     *
     * @param loan                   贷款计划
     * @param planIndex              计划顺序
     * @param beginResidualPrincipal 期初剩余本金
     * @param beginIncreasedInterest 期初累积归还利息
     * @param actRepayDate           实际还款日期
     * @param actRepayTotal          实际还款总金额(利息+本金)
     * @return
     */
    public RepayPlanCal getCalculatePlan(LoanInfo loan, Integer planIndex, BigDecimal beginResidualPrincipal, BigDecimal beginIncreasedInterest, Date actRepayDate, BigDecimal actRepayTotal) {
        return null;
    }


}
